<template>
  <a-upload
    :show-upload-list="false"
    list-type="picture-card"
    :max-count="maxCount"
    class="use-upload"
    :before-upload="beforeUpload">
      <div class="avatar-uploader">
        <span v-if="value">
          <a-tooltip title="点击重新上传">
            <img :src="staticURL+value" alt="avatar" style="width: 100%;"/>
          </a-tooltip>
        </span>
        <div v-else>
          <loading-outlined v-if="loading"></loading-outlined>
          <plus-outlined v-else></plus-outlined>
          <div class="color-gray margin-top-sm">选择图片</div>
        </div>
      </div>
  </a-upload>
  <a-modal v-model:open="cropVisible" title="图片裁剪" @ok="handleCrop" ok-text="确认并上传">
    <a-space align="start" :size="20">
      <VueCropper
          ref="cropperRef"
          :img="cropImage"
          :auto-crop="true"
          :fixed="true"
          :mode="cropModel"
          :fillColor="fillColor"
          :auto-crop-width="cropSize[0]"
          :auto-crop-height="cropSize[1]"
          :fixed-number="fixedNumber"
          style="width: 330px; height: 330px;"
          @realTime="onRealTime"
      />
      <div>
        <p>实时预览</p>
        <a-avatar v-if="preview" :src="preview" shape="square" :size="120"/>
      </div>
    </a-space>
  </a-modal>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue';
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper"
import type { UploadProps } from 'ant-design-vue';
import {uploader} from "@/server/modules/uploader";
import useMessage from '@/hooks/useMessage';
import {LoadingOutlined,PlusOutlined} from '@ant-design/icons-vue'
import {staticURL} from '@/config'
import { fetchCsrfToken } from '@/server/modules/uploader';
onMounted(()=>{
  fetchCsrfToken()
})
/**
 * 定义组件的属性及其默认值
 * text: 设置上传按钮的文本，默认为'上传图片'
 * model: 设置组件的模式，默认为'default'
 * crop: 控制是否开启图片裁剪功能，默认开启
 * cropSize: 设置裁剪框的大小，默认为200x200像素
 * fillColor: 设置裁剪框背景颜色，默认为'#fafafa'
 * cropModel: 设置图片裁剪的模式，默认为'cover'
 * fixedNumber: 设置裁剪框的宽高比例，默认为1:1
 * showPreview: 控制是否显示图片预览，默认显示
 * maxCount: 设置最大上传图片数量，默认为1
 * listType: 设置上传列表的样式类型，默认为'text'
 * cardSize: 设置上传列表中图片卡片的大小，默认为100x100像素
 */
defineProps({
  text:{type:String,default:'上传图片'},
  model:{type:String,default:'default'},
  crop:{type:Boolean,default:true},
  cropSize: {type: Array, default: [200,200]},
  fillColor: {type: String, default: '#fafafa'},
  cropModel: {type: String, default: 'cover'},
  fixedNumber: {type: Array, default: [1,1]},
  showPreview: {type: Boolean, default: true},
  maxCount: {type: Number, default: 1},
  listType:{type:String,default:'text'},
  cardSize: {type: Array, default: [100,100]},
  name: {type: String, default: 'file'},
});
const cropVisible = ref(false);
const cropImage = ref();
const cropperRef = ref();
let preview = ref()
let loading = ref(false)

/**
 * 定义一个响应式数据，用于存储上传的文件路径或URL
 * 该数据将作为组件的绑定值，用于显示已上传的图片或文件
 * @type {string}
 */
const value = defineModel<string>('value')

/**
 * 在上传文件之前执行的钩子函数
 * 该函数主要用于自定义文件上传的逻辑，例如对文件进行裁剪或其他处理
 * 校验文件上传大小为10M
 * @param {File} file - 将要上传的文件对象
 * @returns {boolean} - 返回false以阻止自动上传，以便进行自定义处理
 */
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
  const isLt2M = file.size / 1024 / 1024 < 10;
  if (!isLt2M){
    useMessage(403,'上传图片大小不能超过10M');
    return false
  }
  const reader = new FileReader();
  reader.readAsDataURL(file as Blob);
  reader.onload = () => {
    cropImage.value = reader.result as string;
    cropVisible.value = true;
  };
  return false; // 阻止自动上传
};

/**
 * 处理裁剪图像的函数
 * 该函数将获取裁剪后的图像数据，并将其上传到服务器
 */
const handleCrop = () => {
  cropperRef.value?.getCropBlob(async (blob:Blob)=>{
    const cropFile = new File([blob],'cropped-image.jpg',{
      type: 'image/jpeg'
    })
    const formData = new FormData();
    formData.append('file',cropFile)
    // 使用axios上传图片到服务器
    let res = await uploader(formData)
    if(res.code === 200){
      value.value = res.data.file_url
      cropVisible.value = false
    }
    useMessage(res.code,res.message)
  })
};

/**
 * 实时裁剪函数
 * 该函数用于在不更新裁剪值的情况下，实时获取当前的裁剪效果
 * 主要用于需要实时预览裁剪效果，但不希望立即应用裁剪值的场景
 * 
 * @note 此函数根据vue-cropper的文档实现，但请注意@realTime返回不会刷新裁剪值
 */
const onRealTime = ()=>{
  cropperRef.value.getCropData((data:any)=>{
    preview.value = data
  })
}
</script>

<style scoped>
:deep(.ant-upload-select-picture-card){
  width: 100%;

}
.preview-image {
  width: 100px;
  height: 100px;
}
.ant-upload-text {
  margin-top: 8px;
  color: #666;
}
.avatar-uploader {
  width: 100%;
  height: auto;
  overflow: hidden;
  border-radius: 6px;
}
</style>
